/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> uls_lf_swprintf.c </file>
  <brief>
    An wide-uls_char_t string version of uls_lf_sprintf.c.
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, 2011.
  </author>
*/

#include "common.h"
#include "uls_lf_swprintf.h"

int
fmtproc_ls(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	int wlen, numbuf_len2;
	uls_lf_word_t *wstr;
	size_t siz;
	uls_lf_char_t *cbufptr;

	wstr = (uls_lf_word_t* ) va_arg(ctx->args, uls_lf_word_t *);

	if (wstr==NULL) wstr = L"<NULL>";
	wlen = wcslen(wstr);

	if ((siz = wcstombs(NULL, wstr, 0)) == (size_t) -1) {
		return -1;
	}

	csz_append(ctx->numbuf2, NULL, siz);
	numbuf_len2 = csz_length(ctx->numbuf2);
	cbufptr = (char *) csz_text(ctx->numbuf2) + numbuf_len2;

	if ((siz=wcstombs((char *) cbufptr, wstr, siz+1)) == (size_t) -1) {
		return -1;
	}

	return uls_lf_fill_mbstr(ctx->x_dat, puts_proc, &ctx->perfmt, cbufptr, siz, wlen);
}

int
fmtproc_lc(uls_lf_context_t* ctx, uls_lf_puts_t puts_proc)
{
	uls_lf_char_t buf2[ULS_MB_CUR_MAX+1];
	unsigned int ch;
	uls_lf_word_t  wch;
	int rc;

	ch = (unsigned int) va_arg(ctx->args, unsigned int);

	wch = (uls_lf_word_t) ch;
	if ((rc=wctomb((char *) buf2, wch)) < 0) {
		return -1;
	}

	puts_proc(ctx->x_dat, buf2, rc);
	return rc;
}

static int
__uls_lf_vxwprintf(void *x_dat, uls_lf_t* uls_lf,
	void* g_dat, const uls_lf_word_t* fmtstr, va_list args)
{
	extern int __uls_lf_vxprintf(void *x_dat, uls_lf_t* uls_lf, void* g_dat, const uls_lf_char_t* fmtstr, va_list args);
	int buflen;
	uls_lf_char_t cbuf[256], *cbufptr;
	size_t siz;

	if ((siz=wcstombs(NULL, fmtstr, 0)) == (size_t) -1) {
		return -1;
	}

	if (siz < sizeof(cbuf)) {
		cbufptr = cbuf;
	} else {
		cbufptr = (uls_lf_char_t *) uls_malloc((siz+1)*sizeof(uls_lf_char_t));
	}

	if ((siz=wcstombs((char *) cbufptr, fmtstr, siz+1)) == (size_t) -1) {
		buflen = -1;
	} else {
		buflen = __uls_lf_vxprintf(x_dat, uls_lf, g_dat, cbufptr, args);
	}

	if (cbufptr != cbuf) {
		uls_mfree(cbufptr);
	}

	return buflen;  // # of bytes
}

int
uls_lf_vxwprintf(void *x_dat, uls_lf_t* uls_lf,
	void* g_dat, const uls_lf_word_t* fmtstr, va_list args)
{
	int len;

	uls_lock_mutex(&uls_lf->mtx);
	len = __uls_lf_vxwprintf(x_dat, uls_lf, g_dat, fmtstr, args);
	uls_unlock_mutex(&uls_lf->mtx);

	return len;
}

int
uls_lf_xwprintf(void* x_dat, uls_lf_t* uls_lf, void* g_dat, const uls_lf_word_t* fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vxwprintf(x_dat, uls_lf, g_dat, fmt, args);
	va_end(args);

	return len;
}

int uls_lf_vzwprintf(uls_lf_t* uls_lf, uls_lf_csz_t* bb, void* g_dat, const uls_lf_word_t *fmt, va_list args)
{
	return uls_lf_vsnwprintf_generic(uls_lf, &bb->csz, uls_lf_puts_csz, g_dat, fmt, args);
}

int uls_lf_zwprintf_append(uls_lf_csz_t* bb, void* g_dat, const uls_lf_word_t *fmt, ...)
{
	va_list args;
	int len;

	va_start(args, fmt);
	len = uls_lf_vzwprintf(NULL, bb, g_dat, fmt, args);
	va_end(args);

	return len;
}

int uls_lf_zwprintf(uls_lf_csz_t* bb, void* g_dat, const uls_lf_word_t *fmt, ...)
{
	va_list args;
	int len;

	csz_reset(&bb->csz);

	va_start(args, fmt);
	len = uls_lf_vzwprintf(NULL, bb, g_dat, fmt, args);
	va_end(args);

	return len;
}

int
uls_lf_vsnwprintf_generic(uls_lf_t* uls_lf, void* x_dat, uls_lf_puts_t puts_proc,
	void* g_dat, const uls_lf_word_t* fmtstr, va_list args)
{
	extern uls_lf_puts_t __uls_lf_change_puts(uls_lf_t *uls_lf, uls_lf_puts_t puts_proc);
	uls_lf_puts_t old_proc;
	int len;

	if (uls_lf == NULL) uls_lf = dfl_uls_lf;
	if (puts_proc == NULL) puts_proc = uls_lf_puts_null;

	uls_lock_mutex(&uls_lf->mtx);
	old_proc = __uls_lf_change_puts(uls_lf, puts_proc);
	len = __uls_lf_vxwprintf(x_dat, uls_lf, g_dat, fmtstr, args);
	__uls_lf_change_puts(uls_lf, old_proc);
	uls_unlock_mutex(&uls_lf->mtx);

	return len;
}
